home *** CD-ROM | disk | FTP | other *** search
- unit uContactSync;
-
- {
- *******************************************************************************
- * Descriptions: Main Contact Sync Unit
- * $Source: /cvsroot/fma/fma/uContactSync.pas,v $
- * $Locker: $
- *
- * Todo:
- * - Let the OOD reflect the xml
- * - Filters on the external contacts
- * - Hash sperate items of a contact so less conflicts arise
- * - Do it using interfaces. IIdentifiable INameble IConflictSolver ISynchronizable
- *
- * Change Log:
- * $Log: uContactSync.pas,v $
- * Revision 1.9 2004/07/07 09:39:49 z_stoichev
- * Resolved warnings
- *
- * Revision 1.8 2004/06/26 16:47:09 lordlarry
- * Contacts can be Unlinked
- *
- * Revision 1.7 2004/06/25 18:27:09 lordlarry
- * Added this changelog header
- *
- *
- }
-
- interface
-
- uses
- Contnrs, Classes, SysUtils;
-
- const
- MaxCardinal = High(Cardinal);
-
- type
- ESynchronize = class(Exception);
-
- TContactState = (csUnknown, csUnchanged, csNew, csChanged, csDeleted);
- TContactSollution = (slLeft, slRight, slNeither);
- TContactAction = (caAdd, caUpdate, caDelete, caUnlink);
- TContactActions = set of TContactAction;
-
- TBaseContact = class(TObject)
- private
- FTitle: WideString;
- FCellPhone: WideString;
- FFaxPhone: WideString;
- FOtherPhone: WideString;
- FOrganization: WideString;
- FEmail: WideString;
- FName: WideString;
- FWorkPhone: WideString;
- FSurName: WideString;
- FHomePhone: WideString;
- function GetFullName: WideString;
- public
- property Title: WideString read FTitle write FTitle;
- property Name: WideString read FName write FName;
- property SurName: WideString read FSurName write FSurName;
- property Organization: WideString read FOrganization write FOrganization;
- property Email: WideString read FEmail write FEmail;
- property HomePhone: WideString read FHomePhone write FHomePhone;
- property WorkPhone: WideString read FWorkPhone write FWorkPhone;
- property CellPhone: WideString read FCellPhone write FCellPhone;
- property FaxPhone: WideString read FFaxPhone write FFaxPhone;
- property OtherPhone: WideString read FOtherPhone write FOtherPhone;
-
- property FullName: WideString read GetFullName;
- end;
-
- TContactSource = class;
-
- TContact = class(TBaseContact)
- private
- FSyncID: Cardinal;
- FID: Variant;
- FSyncHash: Cardinal;
- FLinkedContact: TContact;
- FSynchronized: Boolean;
- FContactSource: TContactSource;
- function GetHash: Cardinal;
- protected
- function GetHashString: String; virtual;
- function Exists: Boolean; virtual; abstract;
- public
- constructor Create(ContactSource: TContactSource);
- property ContactSource: TContactSource read FContactSource write FContactSource;
-
- property Synchronized: Boolean read FSynchronized write FSynchronized;
-
- property SyncID: Cardinal read FSyncID write FSyncID;
- property ID: Variant read FID write FID;
- property SyncHash: Cardinal read FSyncHash write FSyncHash;
- property Hash: Cardinal read GetHash;
- property LinkedContact: TContact read FLinkedContact write FLinkedContact;
-
- function IsUnchanged: Boolean;
- function IsNew: Boolean; virtual;
- function IsChanged: Boolean; virtual;
- function IsDeleted: Boolean; virtual;
- function GetContactState: TContactState;
-
- procedure Clone(Value: TContact);
- end;
-
- TContacts = class
- private
- FList: TObjectList;
- function GetItem(Index: Integer): TContact;
- function GetCount: Integer;
- procedure PutItem(Index: Integer; const Value: TContact);
- public
- constructor Create;
- destructor Destroy; override;
-
- function Add(Item: TContact): Integer;
- procedure Clear;
- procedure Delete(Index: Integer);
- procedure Remove(Item: TContact);
- function IndexOf(Item: TContact): Integer;
- property Count: Integer read GetCount;
- property Items[Index: Integer]: TContact read GetItem write PutItem; default;
- function FindByID(ID: Variant): TContact;
- function FindBySyncID(SyncID: Cardinal): TContact;
- end;
-
- TContactSource = class
- private
- FContacts: TContacts;
- FConfirmActions: TContactActions;
- protected
- function GetName: String; virtual; abstract;
- function DeformatPhoneNumber(PhoneNumber: String): String; virtual;
- public
- constructor Create;
- destructor Destroy; override;
-
- property Name: String read GetName;
-
- property Contacts: TContacts read FContacts;
-
- function New: TContact; virtual; abstract;
- function Add(Value: TContact): TContact; virtual; abstract;
- procedure Update(Contact, Value: TContact); virtual; abstract;
- procedure Delete(Contact: TContact); virtual; abstract;
- function Find(SyncID: Cardinal): TContact;
- procedure Unlink(Contact: TContact); virtual;
-
- procedure Load; virtual; abstract;
-
- property ConfirmActions: TContactActions read FConfirmActions write FConfirmActions;
- end;
-
- TPossibleLink = class
- private
- FScore: Integer;
- FContact: TContact;
- public
- property Contact: TContact read FContact write FContact;
- property Score: Integer read FScore write FScore;
- end;
-
- TPossibleLinks = class
- private
- FList: TObjectList;
- function GetItem(Index: Integer): TPossibleLink;
- function GetCount: Integer;
- procedure PutItem(Index: Integer; const Value: TPossibleLink);
- public
- constructor Create;
- destructor Destroy; override;
-
- function Add(Contact: TContact; Score: Integer): Integer;
- procedure Clear;
- procedure Delete(Index: Integer);
- procedure Remove(Item: TPossibleLink);
- function IndexOf(Item: TPossibleLink): Integer;
- property Count: Integer read GetCount;
- property Items[Index: Integer]: TPossibleLink read GetItem write PutItem; default;
- procedure Sort;
- end;
-
- TSyncContactsConflictEvent = procedure(Sender: TObject; Contact: TContact;
- const Description: WideString; const Item0Name, Item1Name: String; var SelectedItem: Integer) of object;
- TSyncContactsFirstTimeEvent = procedure(Sender: TObject; var Continue: Boolean) of object;
- TSyncContactsErrorEvent = procedure(Sender: TObject; const Message: String) of object;
- TSyncContactsConfirmEvent = procedure(Sender: TObject; Contact: TContact; Action: TContactAction;
- const Description: WideString; var Confirmed: Boolean) of object;
- TSyncContactsChooseContactEvent = procedure(Sender: TObject; Contact: TContact; PossibleLinks: TPossibleLinks; var OtherContact: TContact) of object;
-
- TSynchronizeContacts = class
- private
- FFMA: TContactSource;
- FExtern: TContactSource;
- FFileName: String;
- FOnConflict: TSyncContactsConflictEvent;
- FSWitched: Boolean;
- FOnFirstTime: TSyncContactsFirstTimeEvent;
- FOnError: TSyncContactsErrorEvent;
- FOnConfirm: TSyncContactsConfirmEvent;
- FOnChooseLink: TSyncContactsChooseContactEvent;
- procedure DoSynchronize(Left, Right: TContactSource);
- function CalculateLinkScore(Contact, OtherContact: TContact): Integer;
- function FindLink(Contact: TContact; OtherSource: TContactSource): TContact;
- function Conflict(Left, Right: TContact): TContactSollution;
- function Confirm(Action: TContactAction; Source: TContactSource; Contact: TContact): Boolean;
- function BuildCompareDescription(Contact, OtherContact: TContact): WideString;
- function BuildActionDescription(Action: TContactAction; Source: TContactSource; Contact: TContact): WideString;
- function Add(Source: TContactSource; Value: TContact): TContact;
- procedure Update(Source: TContactSource; Contact, Value: TContact);
- procedure Delete(Source: TContactSource; Contact, OtherContact: TContact);
- procedure Link(Contact, OtherContact: TContact);
- protected
- procedure DoConflict(Contact: TContact;
- const Description: WideString; const Item0Name, Item1Name: String;
- var SelectedItem: Integer); virtual;
- function DoFirstTime: Boolean; virtual;
- procedure DoError(const Message: String); virtual;
- procedure DoConfirm(Contact: TContact; Action: TContactAction;
- const Description: WideString; var Confirmed: Boolean); virtual;
- procedure DoChooseLink(Contact: TContact; PossibleLinks: TPossibleLinks; var OtherContact: TContact); virtual;
- public
- property FileName: String read FFileName write FFileName;
- property FMA: TContactSource read FFMA write FFMA;
- property Extern: TContactSource read FExtern write FExtern;
- property OnConflict: TSyncContactsConflictEvent read FOnConflict write FOnConflict;
- property OnFirstTime: TSyncContactsFirstTimeEvent read FOnFirstTime write FOnFirstTime;
- property OnError: TSyncContactsErrorEvent read FOnError write FOnError;
- property OnConfirm: TSyncContactsConfirmEvent read FOnConfirm write FOnConfirm;
- property OnChooseLink: TSyncContactsChooseContactEvent read FOnChooseLink write FOnChooseLink;
-
- procedure Load;
- procedure Synchronize;
- procedure Save;
-
- procedure Unlink(CDID: TGUID);
- end;
-
- procedure SyncLog(const Msg: String);
- procedure SyncLogFmt(const Msg: String; const Args: array of const);
-
- implementation
-
- uses
- Forms, Variants, uXMLContactSync, CRC32, uSyncPhonebook, Unit1;
-
- procedure SyncLog(const Msg: String);
- begin
- Form1.SyncLog(Msg, True);
- end;
-
- procedure SyncLogFmt(const Msg: String; const Args: array of const);
- begin
- SyncLog(Format(Msg, Args));
- end;
-
- { TSynchronizeContacts }
-
- procedure TSynchronizeContacts.DoSynchronize(Left, Right: TContactSource);
- var I: Integer;
- LeftContact, RightContact: TContact;
- LeftState, RightState: TContactState;
- Sollution: TContactSollution;
- begin
- for I := 0 to Left.Contacts.Count - 1 do begin
- LeftContact := Left.Contacts[I];
- if not LeftContact.Synchronized then begin
- LeftState := LeftContact.GetContactState;
-
- RightContact := LeftContact.LinkedContact;
-
- if LeftState = csNew then begin
- RightContact := FindLink(LeftContact, Right);
- if Assigned(RightContact) then
- Link(LeftContact, RightContact)
- else
- Add(Right, LeftContact);
- end
- else begin
- if not Assigned(RightContact) then
- raise ESynchronize.Create('Linked contact not found');
-
- RightState := RightContact.GetContactState;
-
- if LeftState = csChanged then begin
- if RightState = csUnchanged then begin
- Update(Right, RightContact, LeftContact);
- end
- else if RightState = csChanged then begin
- Sollution := Conflict(LeftContact, RightContact);
- if Sollution = slLeft then begin
- Update(Right, RightContact, LeftContact);
- end
- else if Sollution = slRight then begin
- Update(Left, LeftContact, RightContact);
- end;
- end
- else if RightState = csDeleted then begin
- Sollution := Conflict(LeftContact, RightContact);
- if Sollution = slLeft then begin
- Add(Right, LeftContact);
- end
- else if Sollution = slRight then begin
- Delete(Left, LeftContact, RightContact);
- end;
- end;
- end
- else if LeftState = csDeleted then begin
- if RightState = csUnchanged then begin
- Delete(Right, RightContact, LeftContact);
- end
- else if RightState = csChanged then begin
- Sollution := Conflict(LeftContact, RightContact);
- if Sollution = slLeft then begin
- Delete(Right, RightContact, LeftContact);
- end
- else if Sollution = slRight then begin
- Add(Left, RightContact);
- end;
- end;
- end;
- end;
-
- { Allow synchronization to be canceled }
- Application.ProcessMessages;
- // if Form1.FAbortDetected then break;
- end;
- end;
- end;
-
- procedure TSynchronizeContacts.Synchronize;
- begin
- SyncLog('Synchronize started');
- try
- FSwitched := False;
- DoSynchronize(FMA, Extern);
- FSwitched := True;
- DoSynchronize(Extern, FMA);
-
- SyncLog('Synchronize completed');
- except
- on E: ESynchronize do begin
- SyncLogFmt('Synchronize error: %s', [E.Message]);
- DoError(E.Message);
- end;
- end;
- end;
-
- function TSynchronizeContacts.Conflict(Left, Right: TContact): TContactSollution;
- var Contact, OtherContact: TContact;
- SelectedItem: Integer;
- Description: WideString;
- begin
- if FSwitched then begin
- Contact := Right;
- OtherContact := Left;
- end
- else begin
- Contact := Left;
- OtherContact := Right;
- end;
-
- SyncLogFmt('%s has a conflict', [Contact.FullName]);
-
- SelectedItem := 0;
-
- Description := BuildCompareDescription(Contact, OtherContact);
-
- DoConflict(Contact, Description, Contact.ContactSource.Name, Contact.LinkedContact.ContactSource.Name, SelectedItem);
-
- case SelectedItem of
- 0: begin
- if Contact = Left then
- Result := slLeft
- else
- Result := slRight;
- SyncLogFmt('Conflict has been solved in favor of %s', [Contact.ContactSource.Name]);
- end;
- 1: begin
- if Contact = Left then
- Result := slRight
- else
- Result := slLeft;
- SyncLogFmt('Conflict has been solved in favor of %s', [Contact.LinkedContact.ContactSource.Name]);
- end;
- else begin
- Result := slNeither;
- SyncLog('Conflict has not been solved');
- end;
- end;
- end;
-
- procedure TSynchronizeContacts.Load;
- var XMLContactSync: IXMLFmaSyncType;
- XMLContact: IXMLContactType;
- I: Integer;
- FMAContact: TContact;
- ExternContact: TContact;
- begin
- SyncLog('Loading started');
- try
- if FileExists(FFileName) then begin
- XMLContactSync := Loadfmasync(FFileName);
- for I := 0 to XMLContactSync.Count - 1 do begin
- XMLContact := XMLContactSync.Contact[I];
-
- FMAContact := FMA.New;
- FMAContact.SyncID := XMLContact.SyncID;
- FMAContact.ID := XMLContact.FMA.ID;
- FMAContact.SyncHash := StrToInt(XMLContact.FMA.Hash);
- FMA.Contacts.Add(FMAContact);
-
- ExternContact := Extern.New;
- ExternContact.SyncID := XMLContact.SyncID;
- ExternContact.ID := XMLContact.Extern.ID;
- ExternContact.SyncHash := StrToInt(XMLContact.Extern.Hash);
- Extern.Contacts.Add(ExternContact);
-
- FMAContact.LinkedContact := ExternContact;
- ExternContact.LinkedContact := FMAContact;
-
- Application.ProcessMessages;
- end;
-
- SyncLogFmt('Loaded %d contacts from XML', [XMLContactSync.Count]);
- end
- else
- if not DoFirstTime then Abort;
-
- FMA.Load;
- Extern.Load;
-
- SyncLog('Loading completed');
- except
- on E: ESynchronize do begin
- SyncLogFmt('Loading error: %s', [E.Message]);
- DoError(E.Message);
- end;
- end;
- end;
-
- procedure TSynchronizeContacts.Save;
- var XMLContactSync: IXMLFmaSyncType;
- XMLContact: IXMLContactType;
- I: Integer;
- FMAContact: TContact;
- ExternContact: TContact;
- ID: Integer;
- begin
- SyncLog('Saving started');
- try
- XMLContactSync := Newfmasync;
-
- ID := 0;
-
- for I := 0 to FMA.Contacts.Count - 1 do begin
- FMAContact := FMA.Contacts[I];
- ExternContact := FMAContact.LinkedContact;
-
- if Assigned(ExternContact) and (not FMAContact.IsDeleted) and (not ExternContact.IsDeleted) then begin
- XMLContact := XMLContactSync.Add;
- XMLContact.SyncID := ID;
-
- XMLContact.FMA.ID := FMAContact.ID;
- XMLContact.FMA.Hash := '$' + IntToHex(FMAContact.Hash, 8);
-
- XMLContact.Extern.ID := ExternContact.ID;
- XMLContact.Extern.Hash := '$' + IntToHex(ExternContact.Hash, 8);
-
- Inc(ID);
- end;
-
- Application.ProcessMessages;
- end;
-
- XMLContactSync.OwnerDocument.SaveToFile(FFileName);
-
- SyncLog('Saving completed');
- except
- on E: ESynchronize do begin
- SyncLogFmt('Saving error: %s', [E.Message]);
- DoError(E.Message);
- end;
- end;
- end;
-
- procedure TSynchronizeContacts.DoConflict(Contact: TContact; const Description:
- WideString; const Item0Name, Item1Name: String; var SelectedItem: Integer);
- begin
- SelectedItem := 0;
-
- if Assigned(FOnConflict) then
- FOnConflict(Self, Contact, Description, Item0Name, Item1Name, SelectedItem);
-
- if SelectedItem = -1 then
- SelectedItem := 0;
- end;
-
- function TSynchronizeContacts.DoFirstTime: Boolean;
- begin
- Result := True;
-
- if Assigned(FOnFirstTime) then
- FOnFirstTime(Self, Result);
- end;
-
- procedure TSynchronizeContacts.DoError(const Message: String);
- begin
- if Assigned(FOnError) then
- FOnError(Self, Message);
- end;
-
- function TSynchronizeContacts.BuildCompareDescription(Contact, OtherContact:
- TContact): WideString;
- var FullName: WideString;
- begin
- if Contact.FullName <> '' then
- FullName := Contact.FullName
- else
- FullName := OtherContact.FullName;
-
- case Contact.GetContactState of
- csUnchanged:
- Result := WideFormat('%s is unchanged in %s', [FullName, Contact.ContactSource.Name]);
- csNew:
- Result := WideFormat('%s is new in %s', [FullName, Contact.ContactSource.Name]);
- csChanged:
- Result := WideFormat('%s is changed in %s', [FullName, Contact.ContactSource.Name]);
- csDeleted:
- Result := WideFormat('%s is deleted from %s', [FullName, Contact.ContactSource.Name]);
- else
- Result := '';
- end;
-
- case OtherContact.GetContactState of
- csUnchanged:
- Result := Result + WideFormat(' and unchanged in %s', [OtherContact.ContactSource.Name]);
- csNew:
- Result := Result + WideFormat(' and new in %s', [OtherContact.ContactSource.Name]);
- csChanged:
- Result := Result + WideFormat(' and changed in %s', [OtherContact.ContactSource.Name]);
- csDeleted:
- Result := Result + WideFormat(' and deleted from %s', [OtherContact.ContactSource.Name]);
- end;
- end;
-
- function TSynchronizeContacts.BuildActionDescription(Action: TContactAction;
- Source: TContactSource; Contact: TContact): WideString;
- begin
- case Action of
- caAdd:
- Result := WideFormat('%s will be added to %s', [Contact.FullName, Source.Name]);
- caUpdate:
- Result := WideFormat('%s will be updated into %s', [Contact.FullName, Source.Name]);
- caDelete:
- Result := WideFormat('%s will be deleted from %s', [Contact.FullName, Source.Name]);
- else
- Result := '';
- end;
- end;
-
- function TSynchronizeContacts.Confirm(Action: TContactAction; Source: TContactSource; Contact: TContact): Boolean;
- var Description: WideString;
- begin
- SyncLogFmt('Confirmation is asked for %s', [Contact.FullName]);
-
- Description := BuildActionDescription(Action, Source, Contact);
-
- DoConfirm(Contact, Action, Description, Result);
-
- if Result then
- SyncLog('Confirmation is granted')
- else
- SyncLog('Confirmation is not granted');
- end;
-
- procedure TSynchronizeContacts.DoConfirm(Contact: TContact; Action:
- TContactAction; const Description: WideString; var Confirmed: Boolean);
- begin
- Confirmed := True;
- if Assigned(FOnConfirm) then
- FOnConfirm(Self, Contact, Action, Description, Confirmed);
- end;
-
- function TSynchronizeContacts.Add(Source: TContactSource; Value: TContact): TContact;
- begin
- Result := nil;
-
- if caAdd in Source.ConfirmActions then
- if not Confirm(caAdd, Source, Value) then Exit;
-
- Result := Source.Add(Value);
-
- Result.Synchronized := True;
- Value.Synchronized := True;
- SyncLogFmt('%s is added to %s', [Result.FullName, Source.Name]);
- end;
-
- procedure TSynchronizeContacts.Update(Source: TContactSource; Contact, Value: TContact);
- begin
- if caUpdate in Source.ConfirmActions then
- if not Confirm(caUpdate, Source, Value) then Exit;
-
- Source.Update(Contact, Value);
-
- Contact.Synchronized := True;
- Value.Synchronized := True;
- SyncLogFmt('%s is updated into %s', [Contact.FullName, Source.Name]);
- end;
-
- procedure TSynchronizeContacts.Delete(Source: TContactSource; Contact, OtherContact: TContact);
- begin
- if caDelete in Source.ConfirmActions then
- if not Confirm(caDelete, Source, Contact) then Exit;
-
- Source.Delete(Contact);
-
- Contact.Synchronized := True;
- OtherContact.Synchronized := True;
- SyncLogFmt('%s is deleted from %s', [Contact.FullName, Source.Name]);
- end;
-
- procedure TSynchronizeContacts.Link(Contact, OtherContact: TContact);
- begin
- Contact.LinkedContact := OtherContact;
- OtherContact.LinkedContact := Contact;
-
- SyncLogFmt('%s is linked to %s', [Contact.FullName, OtherContact.Name]);
- end;
-
- function TSynchronizeContacts.FindLink(Contact: TContact; OtherSource: TContactSource): TContact;
- var I: Integer;
- OtherContact: TContact;
- OtherState: TContactState;
- PossibleLinks: TPossibleLinks;
- Score: Integer;
- begin
- PossibleLinks := TPossibleLinks.Create;
- try
- for I := 0 to OtherSource.Contacts.Count - 1 do begin
- OtherContact := OtherSource.Contacts[I];
- if Assigned(OtherContact) then begin
- OtherState := OtherContact.GetContactState;
-
- if OtherState = csNew then begin
- Score := CalculateLinkScore(Contact, OtherContact);
- PossibleLinks.Add(OtherContact, Score)
- end;
- end;
- end;
- PossibleLinks.Sort;
-
- OtherContact := nil;
- if PossibleLinks.Count > 0 then
- DoChooseLink(Contact, PossibleLinks, OtherContact);
- Result := OtherContact;
- finally
- PossibleLinks.Free;
- end;
- end;
-
- function TSynchronizeContacts.CalculateLinkScore(Contact, OtherContact: TContact): Integer;
- begin
- Result := 0;
-
- if Contact.Title = OtherContact.Title then
- Inc(Result, 1);
- if Contact.Name = OtherContact.Name then
- Inc(Result, 10);
- if Contact.SurName = OtherContact.SurName then
- Inc(Result, 100);
- if Contact.Organization = OtherContact.Organization then
- Inc(Result, 1);
- if Contact.Email = OtherContact.Email then
- Inc(Result, 100);
- if Contact.HomePhone = OtherContact.HomePhone then
- Inc(Result, 100);
- if Contact.WorkPhone = OtherContact.WorkPhone then
- Inc(Result, 10);
- if Contact.CellPhone = OtherContact.CellPhone then
- Inc(Result, 100);
- if Contact.FaxPhone = OtherContact.FaxPhone then
- Inc(Result, 10);
- if Contact.OtherPhone = OtherContact.OtherPhone then
- Inc(Result, 10);
-
- if Contact.Name = OtherContact.SurName then
- Inc(Result, 100);
- if Contact.SurName = OtherContact.Name then
- Inc(Result, 100);
- end;
-
- procedure TSynchronizeContacts.DoChooseLink(Contact: TContact; PossibleLinks: TPossibleLinks; var OtherContact: TContact);
- begin
- if Assigned(FOnChooseLink) then
- FOnChooseLink(Self, Contact, PossibleLinks, OtherContact);
- end;
-
- procedure TSynchronizeContacts.Unlink(CDID: TGUID);
- var XMLContactSync: IXMLFmaSyncType;
- XMLContact: IXMLContactType;
- I: Integer;
- // FMAContact: TContact;
- // ExternContact: TContact;
- Confirmed: Boolean;
- begin
- SyncLog('Unlinking started');
- try
- if FileExists(FFileName) then begin
- XMLContactSync := Loadfmasync(FFileName);
- for I := 0 to XMLContactSync.Count - 1 do begin
- XMLContact := XMLContactSync.Contact[I];
-
- if IsEqualGUID(StringToGUID(XMLContact.FMA.ID), CDID) then begin
- Confirmed := False;
- DoConfirm(nil, caUnlink, 'Link found. About to unlinking', Confirmed);
-
- if Confirmed then begin
- SyncLogFmt('Link %s found and Unlinked', [GUIDToString(CDID)]);
- XMLContactSync.Delete(I);
- end;
-
- Break;
- end;
-
- Application.ProcessMessages;
- end;
-
- XMLContactSync.OwnerDocument.SaveToFile(FFileName);
- end;
-
- SyncLog('Unlinking completed');
- except
- on E: ESynchronize do begin
- SyncLogFmt('Unlinking error: %s', [E.Message]);
- DoError(E.Message);
- end;
- end;
- end;
-
- { TContact }
-
- procedure TContact.Clone(Value: TContact);
- begin
- inherited;
-
- Title := Value.Title;
- Name := Value.Name;
- SurName := Value.SurName;
- Organization := Value.Organization;
- Email := Value.Email;
- HomePhone := Value.HomePhone;
- WorkPhone := Value.WorkPhone;
- CellPhone := Value.CellPhone;
- FaxPhone := Value.FaxPhone;
- OtherPhone := Value.OtherPhone;
-
- SyncID := Value.SyncID;
- ID := Unassigned;
- SyncHash := Hash;
- end;
-
- constructor TContact.Create(ContactSource: TContactSource);
- begin
- inherited Create;
-
- FContactSource := ContactSource;
-
- FSyncID := MaxCardinal;
- end;
-
- function TContact.GetContactState: TContactState;
- begin
- if IsDeleted then
- Result := csDeleted
- else if IsNew then
- Result := csNew
- else if IsChanged then
- Result := csChanged
- else
- Result := csUnchanged;
- end;
-
- function TContact.GetHash: Cardinal;
- var Str: String;
- begin
- Str := GetHashString;
- Result := CalculateCRC32(Str[1], Length(Str));
- end;
-
- function TContact.GetHashString: String;
- begin
- Result := FTitle + FCellPhone + FFaxPhone + FOtherPhone + FOrganization +
- FEmail + FName + FWorkPhone + FSurName + FHomePhone;
- end;
-
- function TContact.IsChanged: Boolean;
- begin
- Result := FSyncHash <> Hash;
- end;
-
- function TContact.IsDeleted: Boolean;
- begin
- Result := not Exists;
- end;
-
- function TContact.IsNew: Boolean;
- begin
- Result := VarIsEmpty(FID) or not Assigned(FLinkedContact);
- end;
-
- function TContact.IsUnchanged: Boolean;
- begin
- Result := not (IsNew or IsChanged or IsDeleted);
- end;
-
- { TContactSource }
-
- constructor TContactSource.Create;
- begin
- inherited;
-
- FContacts := TContacts.Create;
- FConfirmActions := [caAdd, caUpdate, caDelete];
- end;
-
- function TContactSource.DeformatPhoneNumber(PhoneNumber: String): String;
- const ValidChars = ['*', '#', '+', '0'..'9', 'p'];
- var I: Integer;
- begin
- Result := '';
- for I := 1 to Length(PhoneNumber) do
- if PhoneNumber[I] in ValidChars then
- Result := Result + PhoneNumber[I];
- end;
-
- destructor TContactSource.Destroy;
- begin
- FContacts.Free;
-
- inherited;
- end;
-
- function TContactSource.Find(SyncID: Cardinal): TContact;
- begin
- Result := FContacts.FindBySyncID(SyncID);
- end;
-
- procedure TContactSource.Unlink(Contact: TContact);
- begin
- if Assigned(Contact.LinkedContact) then begin
- Contact.LinkedContact.LinkedContact := nil;
- Contact.LinkedContact := nil;
- end;
- end;
-
- { TContacts }
-
- function TContacts.Add(Item: TContact): Integer;
- begin
- Result := FList.Add(Item);
- end;
-
- procedure TContacts.Clear;
- begin
- FList.Clear;
- end;
-
- constructor TContacts.Create;
- begin
- inherited;
-
- FList := TObjectList.Create;
- end;
-
- procedure TContacts.Delete(Index: Integer);
- begin
- FList.Delete(Index);
- end;
-
- destructor TContacts.Destroy;
- begin
- FList.Free;
-
- inherited;
- end;
-
- function TContacts.GetItem(Index: Integer): TContact;
- begin
- Result := FList[Index] as TContact;
- end;
-
- function TContacts.GetCount: Integer;
- begin
- Result := FList.Count;
- end;
-
- function TContacts.IndexOf(Item: TContact): Integer;
- begin
- Result := FList.IndexOf(Item);
- end;
-
- procedure TContacts.PutItem(Index: Integer; const Value: TContact);
- begin
- FList[Index] := Value;
- end;
-
- procedure TContacts.Remove(Item: TContact);
- begin
- FList.Remove(Item);
- end;
-
- function TContacts.FindByID(ID: Variant): TContact;
- var I: Integer;
- begin
- Result := nil;
-
- for I := 0 to Count - 1 do
- if Items[I].ID = ID then begin
- Result := Items[I];
- Break;
- end;
- end;
-
- function TContacts.FindBySyncID(SyncID: Cardinal): TContact;
- var I: Integer;
- begin
- Result := nil;
-
- for I := 0 to Count - 1 do
- if Items[I].SyncID = SyncID then begin
- Result := Items[I];
- Break;
- end;
- end;
-
- { TBaseContact }
-
- function TBaseContact.GetFullName: WideString;
- begin
- Result := FName;
- if FSurName <> '' then
- if Result <> '' then
- Result := Result + ' ' + FSurName
- else
- Result := FSurName;
- end;
-
- { TPossibleLinks }
-
- constructor TPossibleLinks.Create;
- begin
- inherited;
-
- FList := TObjectList.Create;
- end;
-
- destructor TPossibleLinks.Destroy;
- begin
- FList.Free;
-
- inherited;
- end;
-
- function TPossibleLinks.Add(Contact: TContact; Score: Integer): Integer;
- var PossibleLink: TPossibleLink;
- begin
- PossibleLink := TPossibleLink.Create;
- PossibleLink.Contact := Contact;
- PossibleLink.Score := Score;
-
-
- Result := FList.Add(PossibleLink);
- end;
-
- procedure TPossibleLinks.Clear;
- begin
- FList.Clear;
- end;
-
- procedure TPossibleLinks.Delete(Index: Integer);
- begin
- FList.Delete(Index);
- end;
-
- function TPossibleLinks.GetCount: Integer;
- begin
- Result := FList.Count;
- end;
-
- function TPossibleLinks.GetItem(Index: Integer): TPossibleLink;
- begin
- Result := FList[Index] as TPossibleLink;
- end;
-
- function TPossibleLinks.IndexOf(Item: TPossibleLink): Integer;
- begin
- Result := FList.IndexOf(Item);
- end;
-
- procedure TPossibleLinks.PutItem(Index: Integer; const Value: TPossibleLink);
- begin
- FList[Index] := Value;
- end;
-
- procedure TPossibleLinks.Remove(Item: TPossibleLink);
- begin
- FList.Remove(Item);
- end;
-
- function PossibleLinksSortCompare(Item1, Item2: Pointer): Integer;
- var Score1, Score2: Integer;
- begin
- Score1 := TPossibleLink(Item1).Score;
- Score2 := TPossibleLink(Item2).Score;
-
- if Score1= Score2 then
- Result := 0
- else if Score1 < Score2 then
- Result := 1
- else
- Result := -1;
- end;
-
- procedure TPossibleLinks.Sort;
- begin
- FList.Sort(PossibleLinksSortCompare);
- end;
-
- end.
-